package com.ruyuan.gateway.server.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.AbstractListener;
import com.google.common.collect.Sets;
import com.ruyuan.gateway.sdk.model.ApiAuthorizationRequireInfo;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.web.filter.PathMatchingFilter;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.NamedFilterList;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.Filter;

import java.lang.reflect.Field;
import java.util.*;

import static com.ruyuan.gateway.sdk.model.GatewayConstants.API_FILTER_CHAIN_DATA_ID;
import static com.ruyuan.gateway.sdk.model.GatewayConstants.API_FILTER_CHAIN_GROUP_ID;

/**
 * @author xx
 */
@Component
public class RuyuanFilterChainRefresher extends AbstractListener implements InitializingBean {

    private DefaultFilterChainManager filterChainManager;

    @Autowired
    private AbstractShiroFilter abstractShiroFilter;

    @Resource(name = "gatewayConfigService")
    private ConfigService configService;

    @Override
    public void receiveConfigInfo(String configInfo) {
        if (StringUtils.isEmpty(configInfo)) {
            return;
        }
        List<ApiAuthorizationRequireInfo> apiAuthorizationRequireInfos = JSON.parseArray(configInfo, ApiAuthorizationRequireInfo.class);
        // 刷新filterChain,按照priority排序
        apiAuthorizationRequireInfos.stream().sorted(Comparator.comparingInt(ApiAuthorizationRequireInfo::getPriority)).forEach(
                requireInfo -> {
                    // 保留shiro默认的兜底配置不被nacos覆盖
                    if ("/**".equals(requireInfo.getPath())) {
                        return;
                    }
                    filterChainManager.getFilterChains().remove(requireInfo.getPath());
                    // 当前路径的filter chain定义
                    Set<String> chainDefinition = new HashSet<>();
                    // 如果api制定了需要登陆或者需要的权限code，那都需要登陆校验
                    if (requireInfo.getNeedLogin() || StringUtils.isNotBlank(requireInfo.getRequiredAuthorityCode())) {
                        chainDefinition.add("authc");
                    }
                    if (StringUtils.isNotBlank(requireInfo.getRequiredAuthorityCode())) {
                        chainDefinition.add("perms[api:" + requireInfo.getRequiredAuthorityCode() + "]");
                    }
                    filterChainManager.createChain(requireInfo.getPath(), chainDefinition.isEmpty() ? "anon" : String.join(",", chainDefinition));
                }
        );
        // 删除shiro中存在，但是nacos上没配置的path
        Set<String> existChainNames = filterChainManager.getChainNames();
        Set<String> chainNameToRemove = Sets.newHashSet(existChainNames);
        apiAuthorizationRequireInfos.stream().map(ApiAuthorizationRequireInfo::getPath).forEach(chainNameToRemove::remove);
        // 默认兜底的也不删
        chainNameToRemove.remove("/**");
        chainNameToRemove.forEach(chainName -> {
            filterChainManager.getFilterChains().remove(chainName);
            // 更新PathMatchingFilter所有子类的appliedPaths字段值
            for (NamedFilterList namedFilterList : filterChainManager.getFilterChains().values()) {
                for (Filter filter : namedFilterList) {
                    if (filter instanceof PathMatchingFilter) {
                        // 通过反射获取字段值并修改
                        try {
                            Field field = PathMatchingFilter.class.getDeclaredField("appliedPaths");
                            field.setAccessible(true);
                            Map<String, Object> appliedPaths = (Map<String, Object>) field.get(filter);
                            appliedPaths.remove(chainName);
                        } catch (Exception e) {

                        }
                    }
                }
            }
        });
        // 把默认的/**排到最后，不然所有请求都只会走默认的filter chain
        NamedFilterList defaultFilterChain = filterChainManager.getFilterChains().remove("/**");
        // remove后再重新put就会放到最后
        filterChainManager.getFilterChains().put("/**", defaultFilterChain);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 初始化filterChainManager
        PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) abstractShiroFilter.getFilterChainResolver();
        filterChainManager = (DefaultFilterChainManager) filterChainResolver.getFilterChainManager();
        // 启动后主动拉取配置
        receiveConfigInfo(configService.getConfigAndSignListener(API_FILTER_CHAIN_DATA_ID, API_FILTER_CHAIN_GROUP_ID, 3000, this));
    }
}
